﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using SIEGate.Core;
using GSF;
using SIEGate.Core.Legacy;
using SIEGate.Core.Measurements;
using SIEGate.Core.Measurements.Values;

namespace StressTests1
{
    public class SampleArrayProducingDataProcessor : NetworkCommunicator
    {
        public class NetworkCommunicatorInfo : NetworkCommunicatorInfoBase
        {
            private Dictionary<string, string> m_defaults = new Dictionary<string, string>()
            {
                {"SendInterval", "0"},
                {"SamplesPerLoop", "2"}
            }; 
            public long SendInterval;
            public int SamplesPerLoop;
            public override void ExtractConfiguration(System.Data.DataSet configurationTables)
            {
                base.ExtractConfiguration(configurationTables);
                // Send interval is in thousands of ticks
                SendInterval = long.Parse(DefaultGet("SendInterval"));
                SamplesPerLoop = int.Parse(DefaultGet("SamplesPerLoop"));
            }

            public NetworkCommunicatorInfo()
            {
                SetDefaults(m_defaults);
            }
        }

        private Random m_rng = new Random();
        private bool m_enabled = false;

        public SampleArrayProducingDataProcessor(IMessageBusProxy busProxy, NetworkCommunicatorInfo info) : base(busProxy, info)
        {
            
        }
        public override void Start()
        {
            long sendinterval = ((NetworkCommunicatorInfo)Info).SendInterval;
            double [] samples = new double[((NetworkCommunicatorInfo)Info).SamplesPerLoop];
            Guid outID = Info.OutgoingStreamIDs.First();
            long now;
            long next = DateTime.UtcNow.Ticks;
            int tosend = 0;

            MessageBusProxy.Publish(Info.OutgoingStreamIDs);
            m_enabled = true;
            while (m_enabled)
            {
                now = DateTime.UtcNow.Ticks;
                if (sendinterval > 0)
                {
                    if (now > next)
                    {
                        tosend = (int)((now - next) / sendinterval);
                    }
                }
                else
                {
                    tosend = 1;
                }

                for (int i = 0; i < tosend; i++)
                {
                    for (int j = 0; j < samples.Length; j++)
                    {
                        samples[j] = m_rng.NextDouble();
                    }
                    double [] send = new double[samples.Length];
                    Array.Copy(samples, send, send.Length);
                    MessageBusProxy.Send(new RawData(outID, new DoubleArrayValue(send), now - (tosend/(i+1))));
                }
                Thread.Yield();
            }
        }

        public override void Stop()
        {
            m_enabled = false;
        }
    }

    public class MapSamplesAdapter : AdapterBase
    {
        public class AdapterInfo : AdapterBase.AdapterInfoBase
        {
           public AdapterInfo() : base()
           {
           } 
        }
        public MapSamplesAdapter(IAdapterInfo info)
            : base(info)
        {
            
        }
        protected override void HandleMeasurement(IMeasurement m)
        {
            double[] samples = m.Value.GetRaw();

            int nsent = 0;
            foreach (Guid streamid in Info.OutgoingStreamIDs) // hack we know we're using random numbers so there
            {
                OnEmit(new Measurement(streamid, m.Timestamp, new MeasurementStateFlags(), new DoubleValue(samples[nsent]) ));
                nsent++;
            }
        }
    }

    public class GatherSamplesAdapter : AdapterBase
    {
        public class AdapterInfo : AdapterBase.AdapterInfoBase
        {
        }

        private Dictionary<long, Tuple<int, double[]>> m_sampleConcentration;
 
        public GatherSamplesAdapter(IAdapterInfo info)
            : base(info)
        {
            m_sampleConcentration = new Dictionary<long, Tuple<int, double[]>>();    
        }
        protected override void HandleMeasurement(IMeasurement m)
        {
            long now = DateTime.UtcNow.Ticks;

            // todo: fix hardcoded
            long timeout = Ticks.FromSeconds(1);
            const int nsamples = 2;
            if (m_sampleConcentration.ContainsKey(m.Timestamp))
            {
                Tuple<int, double[]> it = m_sampleConcentration[m.Timestamp];
                it.Item2[it.Item1] = m.Value.GetRaw();
                if (it.Item1 + 1 == nsamples)
                {
                    OnEmit(new Measurement(OutgoingStreamIDs.First(), m.Timestamp, new MeasurementStateFlags(), new DoubleArrayValue(it.Item2)));
                    m_sampleConcentration.Remove(m.Timestamp);
                }
                else
                {
                    m_sampleConcentration[m.Timestamp] = Tuple.Create(it.Item1 + 1, it.Item2);
                }

            }
            else
            {
                m_sampleConcentration[m.Timestamp] = Tuple.Create(1, new double[nsamples] { m.Value.GetRaw(), 0.0d });

            }
            
            foreach (long key in m_sampleConcentration.Keys.ToArray())
            {
                if (now - key > timeout)
                {
                    m_sampleConcentration.Remove(key);
                }
            }
            
        }
    }

    public class SampleCondenserNetworkCommunicator : NetworkCommunicator
    {
        public class NetworkCommunicatorInfo: NetworkCommunicatorInfoBase
        {
            
        }

        struct GroupInfoHolder
        {
            public int Remaining;
            public long SentAt;
            public long StartGetting;
            public long StopGetting;
        }

        private bool m_enabled = false;
        private Dictionary<long, GroupInfoHolder> recvInfo;

        // information
        public long MeasurementCount = 0;
        public long AvgMeasurementLatency = 0;
        public long MinMeasurementLatency = long.MaxValue;
        public long MaxMeasuremntLatency = 0;

        public long CompleteGroups = 0;
        
        public long GroupLatency = 0;
        public long GroupLatencyMax = 0;
        public long GroupLatencyMin = long.MaxValue;

        public long GroupRecvSpread = 0;
        public long MinGroupRecvSpread = long.MaxValue;
        public long MaxGroupRecvSpread = 0;

        public Dictionary<Guid,long> histogram = new Dictionary<Guid, long>(); 

        public SampleCondenserNetworkCommunicator(IMessageBusProxy busProxy, NetworkCommunicatorInfo info)
            : base(busProxy, info)
        {
            recvInfo = new Dictionary<long, GroupInfoHolder>();
        }

        public override void Start()
        {
            long now;
            long latencycalc;
            long latency;
            int nPerGroup = Info.IncomingStreamIDs.Count;
            GroupInfoHolder groupInfo;
            foreach (Guid k in Info.IncomingStreamIDs)
            {
                histogram[k] = 0;
            }

            MessageBusProxy.Subscribe(Info.IncomingStreamIDs);
            m_enabled = true;
            while (m_enabled)
            {
                latencycalc = MeasurementCount * AvgMeasurementLatency;
                IMeasurement[] measurements = MessageBusProxy.Recv();
                now = DateTime.UtcNow.Ticks;
                MeasurementCount += measurements.Length;
                foreach (Measurement m in measurements)
                {
                    histogram[m.StreamID] += 1;
                    histogram[Info.IncomingStreamIDs.First()] += 1;
                    latency = now - m.Timestamp;
                    latencycalc += latency;
                    if (latency < MinMeasurementLatency)
                        MinMeasurementLatency = latency;
                    else if (latency > MaxMeasuremntLatency)
                        MaxMeasuremntLatency = latency;

                    if (recvInfo.ContainsKey(m.Timestamp))
                    {
                        groupInfo = recvInfo[m.Timestamp];
                    }
                    else
                    {
                        groupInfo = new GroupInfoHolder()
                        {
                            Remaining = nPerGroup,
                            SentAt = m.Timestamp, 
                            StartGetting = now
                        };
                        recvInfo[m.Timestamp] = groupInfo;
                    }
                    groupInfo.Remaining--;
                    if (groupInfo.Remaining == 0)
                    {
                        long glatency = 0;
                        long gspread = 0;
                        groupInfo.StopGetting = now;
                        glatency = groupInfo.StopGetting - groupInfo.SentAt;
                        gspread = groupInfo.StopGetting - groupInfo.StartGetting;
                        GroupLatency = ((GroupLatency * CompleteGroups) + glatency) / CompleteGroups + 1;
                        GroupRecvSpread = ((GroupRecvSpread * CompleteGroups) + gspread) / CompleteGroups + 1;
                        CompleteGroups++;
                        if (glatency > GroupLatencyMax)
                            GroupLatencyMax = glatency;
                        if (glatency < GroupLatencyMin)
                            GroupLatencyMin = glatency;
                        if (gspread > MaxGroupRecvSpread)
                            MaxGroupRecvSpread = gspread;
                        if (gspread < MinGroupRecvSpread)
                            MinGroupRecvSpread = gspread;

                        recvInfo.Remove(m.Timestamp);

                    }
                }
                if (MeasurementCount > 0)
                {
                    AvgMeasurementLatency = latencycalc / MeasurementCount;
                }
                // In theory, this should be OK... but we'll want to have a timeout on groups anyway...
            }
        }
        public override void Stop()
        {
            m_enabled = false;
        }
    }
}
